using System.Collections.Generic;
using FireStarter.Base;
using Firestarter.DB.Schema.BO;
using FireStarterModeller.BO;
using FireStarterModeller.Logic;
using FireStarterModeller.Test;
using FireStarterModeller.Test.BO;
using NUnit.Framework;
using Rhino.Mocks;

namespace Firestarter.DB_DomainModel.Logic.Test.DBChangesImporterTests
{
    internal static class TestUtilsDBChangesImporter
    {
        internal static DBChangesImporter CreateDBChangesImporter()
        {
            return CreateDBChangesImporter(MockRepository.GenerateStub<IDBTypeToDMPropertyTypeMapper>());
        }

        internal static DBChangesImporter CreateDBChangesImporter(IDBTypeToDMPropertyTypeMapper propertyTypeMapper)
        {
            DBToDMImporterFactory factory = new DBToDMImporterFactory(propertyTypeMapper, new RelationshipNameGenerator());
            return new DBChangesImporter(factory);
        }

        internal static void SetupFactoryWithObjectIdentityImporterErrorMessages(IDBToDMImporterFactory dbToDMImporterFactory, DBPrimaryKey dbPrimaryKey, List<string> errorMessages)
        {
            IDBToDMObjectIdentityImporter objectIdentityImporter = MockRepository.GenerateStub<IDBToDMObjectIdentityImporter>();
            objectIdentityImporter.Stub(t => t.Import(Arg<DMClass>.Is.Anything, Arg<DBPrimaryKey>.Is.Same(dbPrimaryKey), out Arg<IList<string>>.Out(errorMessages).Dummy))
                .Return(new DMObjectIdentity());
            dbToDMImporterFactory.Stub(t => t.CreateDBToDMObjectIdentityImporter()).Return(objectIdentityImporter).Repeat.Any();
        }

        internal static void SetupFactoryWithUniqueConstraintImporterErrorMessages(IDBToDMImporterFactory dbToDMImporterFactory, DBKey dbKey, List<string> errorMessages)
        {
            IDBToDMUniqueConstraintImporter uniqueConstraintImporter = MockRepository.GenerateStub<IDBToDMUniqueConstraintImporter>();
            uniqueConstraintImporter.Stub(t => t.Import(Arg<DMClass>.Is.Anything, Arg<DBKey>.Is.Same(dbKey), out Arg<IList<string>>.Out(errorMessages).Dummy))
                .Return(new DMUniqueConstraint());
            dbToDMImporterFactory.Stub(t => t.CreateDBToDMUniqueConstraintImporter()).Return(uniqueConstraintImporter).Repeat.Any();
        }

        internal static void SetupFactoryWithRelationshipImporterErrorMessages(IDBToDMImporterFactory dbToDMImporterFactory, DBForeignKey dbForeignKey, List<string> errorMessages)
        {
            IDBToDMRelationshipImporter relationshipImporter = MockRepository.GenerateStub<IDBToDMRelationshipImporter>();
            relationshipImporter.Stub(t => t.Import(Arg<DBForeignKey>.Is.Same(dbForeignKey), out Arg<IList<string>>.Out(errorMessages).Dummy))
                .Return(new DMRelationship());
            dbToDMImporterFactory.Stub(t => t.CreateDBToDMRelationshipImporter()).Return(relationshipImporter).Repeat.Any();
        }

        internal static IDBToDMImporterFactory CreateStandardImporterFactoryStub()
        {
            DBToDMImporterFactory dbToDMImporterFactory =
                new DBToDMImporterFactory(MockRepository.GenerateStub<IDBTypeToDMPropertyTypeMapper>(),new RelationshipNameGenerator());
            IDBToDMImporterFactory standardImporterFactoryStub = MockRepository.GenerateStub<IDBToDMImporterFactory>();
            standardImporterFactoryStub.Stub(t => t.CreateDBChangesImporter())
                .WhenCalled(invocation => invocation.ReturnValue = dbToDMImporterFactory.CreateDBChangesImporter())
                .Return(null);
            standardImporterFactoryStub.Stub(t => t.CreateDBToDMClassImporter())
                .WhenCalled(invocation => invocation.ReturnValue = dbToDMImporterFactory.CreateDBToDMClassImporter())
                .Return(null);
            standardImporterFactoryStub.Stub(t => t.CreateDBToDMObjectIdentityImporter())
                .WhenCalled(invocation => invocation.ReturnValue = dbToDMImporterFactory.CreateDBToDMObjectIdentityImporter())
                .Return(null);
            standardImporterFactoryStub.Stub(t => t.CreateDBToDMPropertyImporter())
                .WhenCalled(invocation => invocation.ReturnValue = dbToDMImporterFactory.CreateDBToDMPropertyImporter())
                .Return(null);
            standardImporterFactoryStub.Stub(t => t.CreateDBToDMRelationshipImporter())
                .WhenCalled(invocation => invocation.ReturnValue = dbToDMImporterFactory.CreateDBToDMRelationshipImporter())
                .Return(null);
            standardImporterFactoryStub.Stub(t => t.CreateDBToDMUniqueConstraintImporter())
                .WhenCalled(invocation => invocation.ReturnValue = dbToDMImporterFactory.CreateDBToDMUniqueConstraintImporter()).
                Return(null);
            return standardImporterFactoryStub;
        }

        internal static DBTable CreateUpdatedTableWithMappedClass()
        {
            DBDatabase database = new DBDatabase();
            DBTable table = new DBTable(TestUtilsShared.GetRandomString()).ApproveForAdd();
            database.Tables.Add(table);
            DMSolution solution = TestUtilsDMSolution.CreateUnsavedValidDMSolution();
            DBChangesImporter importer = CreateDBChangesImporter();
            importer.ImportDBChanges(database, solution);
            
            table.TableComment = TestUtilsShared.GetRandomString();

            return table;
        }

        internal static DBColumn CreateUpdatedColumnWithMappedProperty()
        {
            DBDatabase database = new DBDatabase();
            DBTable table = new DBTable(TestUtilsShared.GetRandomString());
            database.Tables.Add(table.ApproveForAdd());
            DBTableBuilder builder = new DBTableBuilder(table);
            DBColumn column = builder.AddPrimaryKeyColumn().Column;
            column.Nullable = TestUtilsShared.GetRandomBoolean();
            column.ApproveForAdd();
            DMSolution solution = TestUtilsDMSolution.CreateUnsavedValidDMSolution();
            IDBChangesImporter importer = CreateDBChangesImporter();
            importer.ImportDBChanges(database, solution);

            column.Comment = TestUtilsShared.GetRandomString();
            column.DataType = TestUtilsShared.GetRandomString();
            column.IsAutoIncrementing = !column.IsAutoIncrementing;
            column.Nullable = !column.Nullable;
            column.OrdinalPosition = TestUtilsShared.GetRandomInt(2, 5);

            return column;
        }

        internal static DBPrimaryKey CreateUpdatedPrimaryKeyWithMappedObjectIdentity()
        {
            DBDatabase database = new DBDatabase();
            DBTable table = new DBTable(TestUtilsShared.GetRandomString()).ApproveForAdd();
            database.Tables.Add(table);
            DBTableBuilder builder = new DBTableBuilder(table);
            builder.AddPrimaryKeyColumn("ExistingPKColumn").ApproveForAdd().Column.ApproveForAdd();
            DBColumn newPKColumn = builder.AddColumn("NewPKColumn").ApproveForAdd();
            DBPrimaryKey dbPrimaryKey = table.PrimaryKeyDB.ApproveForAdd();
            DMSolution solution = TestUtilsDMSolution.CreateUnsavedValidDMSolution();
            DBChangesImporter importer = CreateDBChangesImporter();
            importer.ImportDBChanges(database, solution);

            dbPrimaryKey.PrimaryKeyColumns[0].Column = newPKColumn;

            return dbPrimaryKey;
        }

        internal static DBKey CreateUpdatedAlternateKeyWithMappedUniqueConstraint()
        {
            DBDatabase database = new DBDatabase();
            DBTable table = new DBTable(TestUtilsShared.GetRandomString()).ApproveForAdd();
            database.Tables.Add(table);
            DBTableBuilder builder = new DBTableBuilder(table);
            builder.AddUniqueKeyColumn("ExistingAKColumn").ApproveForAdd();
            DBColumn newAKColumn = builder.AddColumn("NewAKColumn").ApproveForAdd();
            DBKey dbKey = table.Keys[0].ApproveForAdd();
            DMSolution solution = TestUtilsDMSolution.CreateUnsavedValidDMSolution();
            DBChangesImporter importer = CreateDBChangesImporter();
            importer.ImportDBChanges(database, solution);

            dbKey.KeyColumns[0].Column = newAKColumn;

            return dbKey;
        }

        internal static DBForeignKey CreateForeignKeyWithMappedRelationship()
        {
            DBDatabase database = new DBDatabase();
            string relatedTableName = TestUtilsShared.GetRandomString();
            DBTable relatedTable = new DBTable(relatedTableName).ApproveForAdd();
            database.Tables.Add(relatedTable);
            string ownerTableName = TestUtilsShared.GetRandomString();
            DBTable ownerTable = new DBTable(ownerTableName).ApproveForAdd();
            database.Tables.Add(ownerTable);

            DBTableBuilder relatedTableBuilder = new DBTableBuilder(relatedTable);
            relatedTableBuilder.AddPrimaryKeyColumn().ApproveForAdd();

            DBTableBuilder ownerTableBuilder = new DBTableBuilder(ownerTable);
            DBForeignKey foreignKey = ownerTableBuilder.AddForeignKey(TestUtilsShared.GetRandomString(), relatedTable).ApproveForAdd();
            foreignKey.ForeignKeyColumns[0].ApproveForAdd().ParentColumn.ApproveForAdd();
            foreignKey.ForeignKeyColumns[0].ApproveForAdd().RelatedColumn.ApproveForAdd();

            DMSolution solution = TestUtilsDMSolution.CreateUnsavedValidDMSolution();
            DBChangesImporter importer = CreateDBChangesImporter();
            importer.ImportDBChanges(database, solution);

            return foreignKey;
        }

        internal static DBForeignKey CreateUpdatedForeignKeyWithMappedRelationship()
        {
            DBForeignKey foreignKey = CreateForeignKeyWithMappedRelationship();
            DBTable parentTable = (DBTable)foreignKey.ParentTable;
            DMClass parentClass = parentTable.GetMappedDMClass();
            
            DBColumn newFKColumn = new DBColumn("NewFKColumn");
            parentTable.Columns.Add(newFKColumn);
            DMProperty newFKProperty = new DMProperty {MappedDBColumn = newFKColumn};
            parentClass.Properties.Add(newFKProperty);

            foreignKey.ForeignKeyColumns[0].ParentColumn = newFKColumn;

            return foreignKey;
        }
    }

    internal static class TestUtilsDBChangesImporter_Extenstions
    {
        public static void AssertIsBackToApprovedAndPendingChangeTypeIsNone(this IMergableBo mergableBO)
        {
            Assert.IsTrue(mergableBO.PendingChangeApproved);
            Assert.AreEqual(ChangeType.None, mergableBO.PendingChangeType);
        }

        public static void AssertIsImported(this IMergableBo mergableBO)
        {
            mergableBO.AssertIsBackToApprovedAndPendingChangeTypeIsNone();
        }
        public static void AssertIsNotImported(this IMergableBo mergableBO)
        {
            Assert.IsTrue(mergableBO.PendingChangeApproved, "Pending Change Approved Should be true");
            Assert.AreNotEqual(ChangeType.None, mergableBO.PendingChangeType);
        }
        public static void AssertIsApprovedAndPendingChangeTypeIs(this IMergableBo mergableBO, ChangeType changeType)
        {
            Assert.IsTrue(mergableBO.PendingChangeApproved, "Pending Change Approved Should be true");
            Assert.AreEqual(changeType, mergableBO.PendingChangeType);
        }

        public static void AssertIsUnApprovedAndPendingChangeTypeIs(this IMergableBo mergableBO, ChangeType changeType)
        {
            AssertPendingChangeIsNotApproved(mergableBO);
            AssertPendingChangeTypeIs(mergableBO, changeType);
        }

        public static void AssertPendingChangeTypeIs(this IMergableBo mergableBO, ChangeType changeType)
        {
            Assert.AreEqual(changeType, mergableBO.PendingChangeType, "Pending Change Type should be '" + changeType + "'");
        }

        public static void AssertPendingChangeIsNotApproved(this IMergableBo mergableBO)
        {
            Assert.IsFalse(mergableBO.PendingChangeApproved, "Pending Change Approved Should be false");
        }

        public static T ApproveForAdd<T>(this T mergableBo) where T : IMergableBo
        {
            mergableBo.PendingChangeApproved = true;
            mergableBo.PendingChangeType = ChangeType.Add;
            return mergableBo;
        }

        public static T UnApproveForAdd<T>(this T mergableBo) where T : IMergableBo
        {
            mergableBo.PendingChangeApproved = false;
            mergableBo.PendingChangeType = ChangeType.Add;
            return mergableBo;
        }

        public static T ApproveForUpdate<T>(this T mergableBo) where T : IMergableBo
        {
            mergableBo.PendingChangeApproved = true;
            mergableBo.PendingChangeType = ChangeType.Update;
            return mergableBo;
        }

        public static T UnApproveForUpdate<T>(this T mergableBo) where T : IMergableBo
        {
            mergableBo.PendingChangeApproved = false;
            mergableBo.PendingChangeType = ChangeType.Update;
            return mergableBo;
        }

        public static T ApproveForDelete<T>(this T mergableBo) where T : IMergableBo
        {
            mergableBo.PendingChangeApproved = true;
            mergableBo.PendingChangeType = ChangeType.Delete;
            return mergableBo;
        }

        public static T UnApproveForDelete<T>(this T mergableBo) where T : IMergableBo
        {
            mergableBo.PendingChangeApproved = false;
            mergableBo.PendingChangeType = ChangeType.Delete;
            return mergableBo;
        }

        public static T ApproveForRename<T>(this T mergableBo) where T : IMergableBo
        {
            mergableBo.PendingChangeApproved = true;
            mergableBo.PendingChangeType = ChangeType.Rename;
            return mergableBo;
        }

        public static T UnApproveForRename<T>(this T mergableBo) where T : IMergableBo
        {
            mergableBo.PendingChangeApproved = false;
            mergableBo.PendingChangeType = ChangeType.Rename;
            return mergableBo;
        }

        public static T ApproveForNone<T>(this T mergableBo) where T : IMergableBo
        {
            mergableBo.PendingChangeApproved = true;
            mergableBo.PendingChangeType = ChangeType.None;
            return mergableBo;
        }

        public static T UnApproveForNone<T>(this T mergableBo) where T : IMergableBo
        {
            mergableBo.PendingChangeApproved = false;
            mergableBo.PendingChangeType = ChangeType.None;
            return mergableBo;
        }
    }
}